package com.fzg.common.demo.GUI.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

/*
  create by fzg
  2022/11/15 13:39
 */

/**
 * 游戏面板
 * 键盘监听器
 * 定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    // 游戏当前的状态
    boolean isStart = false; // 默认不开始
    // 游戏失败，默认FALSE
    boolean isFail = false;
    // 初始化成绩
    int score = 0;
    Snake snake = new Snake();

    // 定时器（小蛇快慢）
    Timer timer = new Timer(500,this);

    // 食物坐标
    int foodx;
    int foody;

    // 前一个按键
    String prePressKey = "";

    Random random = new Random();

    public GamePanel(){
        // 获取焦点
        this.setFocusable(true);
        // 获取键盘的监听事件
        this.addKeyListener(this);
        timer.start();
        createFood();
    }

    /**
     * 绘制面板，
     * 绘制游戏的画面
     */
    @Override
    protected void paintComponent(Graphics g){
        // 清屏，没有这段代码就会不停闪屏
        super.paintComponent(g);
        // 绘制静态面板，背景颜色：白色
        this.setBackground(Color.WHITE);
        // 默认游戏界面
        g.fillRect(25,75,850,600);

        // 食物
        Data.food.paintIcon(this,g,foodx,foody);

        // 画积分
        g.setColor(Color.blue);
        g.setFont(new Font("微软雅黑",Font.BOLD,18));
        g.drawString("长度: ",750,35);
        g.drawString("分数: ",750,53);
        g.setColor(Color.RED);
        g.drawString(snake.length + "",800,35);
        g.drawString(score + "",800,53);

        // 画小蛇
        for (int i = 0; i < snake.length; i++) {
            if (i == 0){
                // 画脑袋
                Data.header.paintIcon(this,g,snake.snakex[i],snake.snakey[i]);
            }else {
                Data.body.paintIcon(this,g,snake.snakex[i],snake.snakey[i]);
            }
        }

        // 游戏未开始时
        if (!isStart){
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格开始游戏", 200,300);
            // 说明一下：请确保键盘的WSAD键处于英文状态下
            g.setColor(Color.GREEN);
            g.setFont(new Font("微软雅黑",Font.PLAIN,25));
            g.drawString("请确保键盘上的WSAD键处于英文状态",200,350);
            g.drawString("除了键盘上的WSAD键，还可以按up,down,left,right键",200,400);
        }

        // 游戏失败
        if (isFail){
            g.setColor(Color.RED);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("游戏结束",300,200);
            g.setFont(new Font("微软雅黑",Font.PLAIN,30));
            g.drawString("按下空格重新开始游戏", 300,300);
            timer.setDelay(500);
        }

    }

    /**
     * Invoked when an action occurs.
     *
     * @param e
     *
     * 事件监听
     * 通过固定事件监听
     */
    @Override
    public void actionPerformed(ActionEvent e) {

        if (isStart && !isFail){
            // 如果游戏状态是开始，就让小蛇动起来
            for (int i = snake.length - 1; i > 0; i--) {
                snake.snakex[i] = snake.snakex[i - 1];
                snake.snakey[i] = snake.snakey[i - 1];
            }
            // 走向
            switch (snake.fx){
                case "D":
                    snake.snakex[0] = snake.snakex[0] + 25;
                    // 边界判断（撞墙就死）
                    if (snake.snakex[0] > 850){
                        // 这是小蛇撞墙不会死，从另一边出来
                        // snake.snakex[0] = 25;
                        // 让小蛇撞墙就game over
                        isFail = true;
                    }
                    break;
                case "A":
                    snake.snakex[0] = snake.snakex[0] - 25;
                    // 边界判断（撞墙就死）
                    if (snake.snakex[0] < 25){
                        // snake.snakex[0] = 850;
                        isFail = true;
                    }
                    break;
                case "W":
                    snake.snakey[0] = snake.snakey[0] - 25;
                    // 边界判断（撞墙就死）
                    if (snake.snakey[0] < 75){
                        // snake.snakey[0] = 650;
                        isFail = true;
                    }
                    break;
                case "S":
                    snake.snakey[0] = snake.snakey[0] + 25;
                    // 边界判断（撞墙就死）
                    if (snake.snakey[0] > 650){
                        // snake.snakey[0] = 75;
                        isFail = true;
                    }
                    break;
            }

            // 把下面的代码注释，让小蛇可以掉头，并且吃到自己的身体也不会死
            // 判断失败，撞到自己就算失败
//            for (int i = 1; i < snake.length; i++) {
//                if (snake.fx.equals("W") && prePressKey.equals("S")){
//                    isFail = false;
//                }else if (snake.fx.equals("S") && prePressKey.equals("W")){
//                    isFail = false;
//                }else if (snake.fx.equals("A") && prePressKey.equals("D")){
//                    isFail = false;
//                }else if(snake.fx.equals("D") && prePressKey.equals("A")){
//                    isFail = false;
//                }else {
//                    if (snake.snakex[0] == snake.snakex[i] && snake.snakey[0] == snake.snakey[i]) {
//                        isFail = true;
//                    }
//                }
//            }

            // 吃到食物
            if (snake.snakex[0] == foodx && snake.snakey[0] == foody){
                // 长度加1
                snake.length++;
                // 加分
                score = addCarry(score,10);

                /*
                  分数等级：
                  0 - 50：原速度      0.5s
                  50 - 100：-100毫秒  0.4s
                  100 - 150：-100毫秒  0.3s
                  150 - 200：-100毫秒  0.2s
                  200 - 250: -100毫秒  0.1s
                  250 - 以上: -50毫秒   0.05s 最快速度了
                 */
                if (score >= 50 && score < 100){
                    timer.setDelay(400);
                }else if (score >= 100 && score < 150){
                    timer.setDelay(300);
                }else if (score >= 150 && score < 200){
                    timer.setDelay(200);
                }else if (score >= 200 && score < 250){
                    timer.setDelay(100);
                }else if (score >= 250){
                    timer.setDelay(50);
                }

                // 再次生成食物
                createFood();
            }
            // 重画页面
            repaint();
        }
        // 开启定时
        timer.start();
    }

    /**
     * 生成食物
     *  g.fillRect(25,75,850,600); 界面的位置和宽高，
     *  游戏界面坐标：25,75 ；距离左边25，距离上边75
     *  宽度：850 ； 高度： 600
     *  食物必须随机生成在游戏界面内
     *  所以：食物的x坐标 必须在25 到 850 + 25之间，
     *  如果刚好等于850 + 25那么食物就生成在游戏界面的右边所以不能等于850 + 25
     *  y坐标也是
     *  所以食物的生成坐标区间
     *  x: [25,850]
     *  y: [75,675-25]
     */
    public void createFood(){
        /*
          把食物随机分配到界面上
          random.nextInt生成随机整数
          850 / 25 = 34 最大值
          650 / 25 = 26 最大值
         */
        foodx = 25 + 25 * random.nextInt(34);
        foody = 75 + 25 * random.nextInt(26);
    }

    /**
     * 位运算实现相加
     */
    int addCarry(int a,int b){
        int result;
        int sum,carry;
        sum = a ^ b;
        carry = (a & b) << 1;
        if (carry == 0){
            result = sum;
        }else {
            result = addCarry(sum,carry);
        }
        return result;
    }


    /**
     * Invoked when a key has been typed.
     * See the class description for {@link KeyEvent} for a definition of
     * a key typed event.
     *
     * @param e 参数
     */
    @Override
    public void keyTyped(KeyEvent e) {

    }

    /**
     * Invoked when a key has been pressed.
     * See the class description for {@link KeyEvent} for a definition of
     * a key pressed event.
     * 键盘按下
     * @param e 参数
     */
    @Override
    public void keyPressed(KeyEvent e) {
        // 获取键盘按键是哪个
        int keyCode = e.getKeyCode();
        /*
          上：38
          下：40
          左：37
          右：39
         */
        if (keyCode == KeyEvent.VK_SPACE){
            // 按下的是空格
            if (isFail){
                // 重新开始
                isFail = false;
                snake.init();
                createFood();
                score = 0;
            }else {
                isStart = !isStart;
            }
            repaint();
        }

        // 小蛇移动
        switch (keyCode){
            case KeyEvent.VK_W:
            case 38:
                snake.fx = "W";
                break;
            case KeyEvent.VK_S:
            case 40:
                snake.fx = "S";
                break;
            case KeyEvent.VK_A:
            case 37:
                snake.fx = "A";
                break;
            case KeyEvent.VK_D:
            case 39:
                snake.fx = "D";
                break;
        }
        prePressKey = snake.fx;
    }

    /**
     * Invoked when a key has been released.
     * See the class description for {@link KeyEvent} for a definition of
     * a key released event.
     *
     * @param e 参数
     */
    @Override
    public void keyReleased(KeyEvent e) {

    }
}
